#include<linux/i2c.h>
#include<linux/backlight.h>
#include<linux/module.h>


#define DEBUG(fmt,arg...)          do{\
    printk("<<-luo-gp7101->> [%d]"fmt"\n",__LINE__, ##arg);\
    }while(0)

struct gp7101_data {
    int max_brightness;
    int default_brightness_level;
    struct i2c_client * client;
};

int update_status(struct backlight_device * bdev){

    struct gp7101_data *data = bl_get_data(bdev);

    struct i2c_client *client = data->client;

    struct backlight_properties *props = &bdev->props;
    // 转换为占空比的亮度
    unsigned int brightness_duty_cycle;

    char buf[3] = {
        0x02, // 16位PWM模式
    };

 

    mutex_lock(&bdev->update_lock);

    brightness_duty_cycle = props->brightness / props->max_brightness * 0xffff;

    /*
        将DATA数据分为DATA Low和DATA High写入，
        DATA Low 为低Byte，DATA High为高Byte。
        输出相对应的PWM的占空比为DATA/0xFFFF
    */
    // 低8位
    buf[1] = brightness_duty_cycle & 0xff;

    // 高8位
    buf[2] = brightness_duty_cycle >>8 ;

    i2c_master_send(client,buf,sizeof(buf));


    mutex_unlock(&bdev->update_lock);
    return 0;
}

int get_brightness(struct backlight_device * bdev){
    // gp7101 没有读取占空比的接口，直接返回 bdev中存储的亮度
    return bdev->props.brightness;
}

int check_fb(struct backlight_device * bdev, struct fb_info * fb_inf){

    return 1;
}

struct backlight_ops gp7101_backlight_ops = {
    

    /* Notify the backlight driver some property has changed */
    .update_status = update_status,
    /* Return the current backlight brightness (accounting for power,
        fb_blank etc.) */
    .get_brightness=get_brightness,
    /* Check if given framebuffer device is the one bound to this backlight;
        return 0 if not, !=0 if it is. If NULL, backlight always matches the fb. */
    .check_fb=check_fb
};



int probe(struct i2c_client *client, const struct i2c_device_id * device_id){

    struct backlight_properties props;
    struct gp7101_data *data;
    struct device *dev = &client->dev;
    struct backlight_device * backlight_dev;
    int result;

    data = devm_kmalloc(dev,sizeof(*data),GFP_KERNEL);
    data->client = client;
    memset(&props, 0, sizeof(struct backlight_properties));
	props.type = BACKLIGHT_RAW;

    if(of_property_read_u32(dev->of_node, "max-brightness",
        &data->max_brightness)){
        DEBUG("failed to fetch max-brightness");
        props.max_brightness = 255;
        data->max_brightness = 255;

    }else{

        props.max_brightness = data->max_brightness;
    }

    if(of_property_read_u32(dev->of_node, "default-brightness-level",
        &data->default_brightness_level)){
        DEBUG("failed to fetch default-brightness-level");
        data->default_brightness_level = data->max_brightness ;
    }

    /*result = test_gp7101(client);
	if(result){
        return result;
    }*/

    /**
     * 使用导出的库函数
     * @see drivers/video/backlight/backlight.c
     * 
     * backlight_device_register - create and register a new object of
     *   backlight_device class.
     * @name: the name of the new object(must be the same as the name of the
     *   respective framebuffer device).
     * @parent: a pointer to the parent device
     * @devdata: an optional pointer to be stored for private driver use. The
     *   methods may retrieve it by using bl_get_data(bd).
     * @ops: the backlight operations structure.
     *
     * Creates and registers new backlight device. Returns either an
     * ERR_PTR() or a pointer to the newly allocated device.
     */
    backlight_dev = devm_backlight_device_register(dev,
        dev_name(dev),
        dev,
        data,
        &gp7101_backlight_ops,
        &props 
    );

    if(IS_ERR(backlight_dev)){
        DEBUG("failed to register backlight device");
        return PTR_ERR(backlight_dev);
    }

    
	backlight_dev->props.brightness = data->default_brightness_level;
	backlight_dev->props.power = 0;
	result = backlight_update_status(backlight_dev);
    if(result){
        return result;
    }

    i2c_set_clientdata(client,data);

    return 0;
}

int remove(struct i2c_client * client){
    return 0;
}

const struct of_device_id gp7101_of_match_table[]={
    {
        .compatible = "luo,gp7101_backlight"
    },{

    }
};

struct i2c_driver gp7101_driver = {
    .probe=probe,
    .remove=remove,
    .driver={
        .of_match_table = gp7101_of_match_table
    }
};

MODULE_DEVICE_TABLE(of, gp7101_of_match_table);

module_i2c_driver( gp7101_driver);


MODULE_DESCRIPTION("i2c PWM based Backlight Driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("i2c:pwm-backlight");
